Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[2 of 4] Builder operates on any HugrMut #281

Closed
wants to merge 19 commits into from
Closed

Conversation

acl-cqc
Copy link
Contributor

@acl-cqc acl-cqc commented Jul 18, 2023

[note: this refers to the situation in commit 42d5eef. It has changed, hopefully for the better, since...]
Via a new Buildable trait, implemented for Hugr, &mut Hugr and &mut T for any T implementing HugrMut.

The end goal (not in this PR) is to allow Rewrites to work on any HugrMut, which in itself I am proposing as a way of allowing Rewrites to be invoked on a region of the Hugr (some future PR can impl HugrMut for FlatRegionView or similar.)

The changes here were mostly straightforward, but

  • It requires some nasty/grotesque syntax: <<<Self as Container>::Base as Buildable>::BaseMut<'_>> kind of thing. I'm wondering if this can be reduced by declaring trait Container: Buildable, say. (Of course, all this would go away if we'd accept a method returning dyn HugrMut....)
  • We have to make HugrMut public, which isn't ideal. But I guess we need to have some trait to indicate a Hugr or mutable view thereof - perhaps a sealed one?
  • The non-straightforward bit is that there are some long impl<T: HugrMut> HugrMut for &mut T blocks which are very obvious/tedious (3 of them - 1 for HugrMut and 2 for HugrView, so the latter 2 could be combined via a macro I guess)

@acl-cqc acl-cqc changed the title Builder operates on any HugrMut [2 of 4] Builder operates on any HugrMut Jul 18, 2023
Base automatically changed from refactor/rewrites_use_hugrmut_meths to main July 24, 2023 13:40
@acl-cqc acl-cqc requested review from aborgna-q and ss2165 July 25, 2023 09:13
@acl-cqc
Copy link
Contributor Author

acl-cqc commented Jul 25, 2023

So I think it should be possible to...

  • Make Buildable method return &mut BaseMut (rather than directly BaseMut).
    • I think I tried this before and ran into the problem that it was impossible to get out the Hugr (if one has a Buildable<BaseMut=Hugr>), but maybe that's possible by a separate trait/method. This may be the biggest problem...
    • Then we can drop impl <T:HugrMut> HugrMut for &mut T, hurrah
  • Remove BaseView, just return an immutable &BaseMut. (HugrMut: HugrView after all so that should suffice). This might allow removing some of the other BaseView impl blocks.
  • Probably, make Buildable a supertrait of Container (and any other similar flattenings that may be possible)

...and hopefully that'll make this palatable enough.

That's the plan, anyway...we'll see...

@acl-cqc
Copy link
Contributor Author

acl-cqc commented Jul 25, 2023

  • Make Buildable method return &mut BaseMut (rather than directly BaseMut).

Done. That seemed straightforward, didn't encounter any problem with retrieving the Hugr

  • Then we can drop impl <T:HugrMut> HugrMut for &mut T, hurrah

No. I did eventually drop the impl <T:HugrView+HugrInternals> HugrView for &T (keeping the same for &mut T), so that leaves only one nasty impl for each of HugrMut/HugrView.

  • Remove BaseView, just return an immutable &BaseMut. (HugrMut: HugrView after all so that should suffice).

That worked ok; BaseMut + BaseView => Base. Not sure what I meant by "This might allow removing some of the other BaseView impl blocks."...

  • Probably, make Buildable a supertrait of Container (and any other similar flattenings that may be possible)

This saved a massive amount of <<<Self as X>::Foo as Y>::Bar> stuff and makes everything reasonably readable. It probably means that we are passing around crazy references to references to references (all hidden behind those magic impl<T:HugrMut> HugrMut for &mut T), but it looks nice at least ;-)

So, please fire away, if you see a better way to do this then I'm game :-)

Copy link
Collaborator

@aborgna-q aborgna-q left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some code-specific comments. Let's discuss trait visibility irl

src/hugr/view.rs Outdated
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The impls for ref could use the delegate! macro (Same thing in hugrmut.rs). See

https://github.com/CQCL/portgraph/blob/5b181e871e975da25f7fc0c148b33b9097c827cf/src/view/filter.rs#L156-L173

@@ -1,3 +1,4 @@
#![allow(missing_docs)]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤨

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this is not for submission, but good to flag up, sorry. If we decide to go this way then I'll take this out and put in docs, but at least it shows that the rest of CI passes

pub fn case_builder(
&mut self,
case: usize,
) -> Result<CaseBuilder<&mut <Self as Buildable>::Base>, BuildError> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this

Suggested change
) -> Result<CaseBuilder<&mut <Self as Buildable>::Base>, BuildError> {
) -> Result<CaseBuilder<&mut B>, BuildError> {

?
(Same thing in other similar structs).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, yeah, thank you! That definitely helps :-)

@acl-cqc
Copy link
Contributor Author

acl-cqc commented Jul 25, 2023

Seyon wondered if we could make Buildable == HugrMut. However, then we need many types to implement HugrMut directly (that presently implement Buildable, i.e. they provide methods producing a HugrMut). I've tried a blanket impl<T: AsMut<HugrMut>> HugrMut for T but that doesn't work as you can't have AsMut<HugrMut> (only AsMut<dyn HugrMut> or AsMut<some concrete type implementing HugrMut>), and my attempt at impl<T: HugrMut, M: AsMut<HugrMut>> HugrMut for M failed with this rust error which does appear to say you can't do that. Any ideas @aborgna-q ?

@acl-cqc
Copy link
Contributor Author

acl-cqc commented Jul 25, 2023

The problem with where we are now is that every time you create a new sub-builder (which internally calls HugrMut), we increase the level of indirection - originally you had a ModuleBuilder<Hugr>, say, then a FunctionBuilder<&mut Hugr>, then a CFGBuilder<&mut &mut Hugr> and inside that a BlockBuilder<&mut &mut &mut Hugr>. Every different <instantiation> is a fresh copy of the code in binary, and every level of &mut is probably another dereference on every call (to anything that uses the hugr_mut(), e.g. all the HugrMut methods)

So I think actually 42d5eef (returning Self::BaseMut) may have been the right thing, as I think that allows an XXXBuilder<&mut Hugr> to create a sub-builder that is also <&mut Hugr> and the number of levels of indirection does not increase....

…direction

No blanket impl<F: Foo> Foo for &[mut] F (for Foo=HugrMut/HugrView/Buildable)
Implement Buildable for <T: AsRef<Hugr> + AsMut<Hugr>>
Buildable does not require HugrMut (too awkward as needs many impls)
Revert hugrmut.rs and view.rs to branch main
@acl-cqc
Copy link
Contributor Author

acl-cqc commented Jul 25, 2023

Ok, major changes, somewhat of a reversion. We are back to separate BaseMut and BaseView so that these types can include a reference only when necessary, thus we don't have to add a level of indirection each time we call hugr_mut().

In particular there are no impl<H:HugrMut> HugrMut for &mut H or similiar for HugrView/Buildable - there is the original impl<T:AsMut<Hugr> + AsRef<Hugr>> HugrMut for T as per branch main only (and the same for Buildable). Thus when you see a call to hugr_mut() you know it goes through at most one level of indirection, somewhat like 42d5eef and as opposed to the unlimited number of indirections possible in the previous version.

@acl-cqc acl-cqc requested a review from aborgna-q July 25, 2023 18:45
@@ -29,17 +30,40 @@ use crate::Hugr;

use crate::hugr::HugrMut;

pub trait Buildable {
type BaseMut<'a>: Buildable + HugrMut
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could avoid this Buildable + HugrMut everywhere if we wrote a blanket impl<B:Buildable> HugrMut for B (just by delegating to self.hugr_mut()). That would let us just constrain everything by Buildable instead, which would be nicer. OTOH it means one can execute HugrMut methods on every Buildable, of which there are many (every Container, etc.)....maybe that'd be a good thing (and could be done as a follow-up PR if we want to see what it looks like?)

fn hugr(&self) -> Self::BaseView<'_>;
}

impl<T: AsMut<Hugr> + AsRef<Hugr>> Buildable for T {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An interesting alternative (in that it illustrates what's going on) would be one impl Buildable for Hugr and a second impl<T: HugrMut + Copy> Buildable for T - as every &mut is Copy (right?!). That's two impl blocks rather than one though so I didn't...

@@ -25,24 +25,28 @@ pub struct CFGBuilder<T> {
pub(super) n_out_wires: usize,
}

impl<B: AsMut<Hugr> + AsRef<Hugr>> Container for CFGBuilder<B> {
impl<B: Buildable> Buildable for CFGBuilder<B> {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are, indeed, a lot of impl blocks like this. I guess these could be done via a macro...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, if/presuming that works, one could have a macro implementing HugrMut for all of these, which would allow combining Buildable into HugrMut. (If we want HugrMut-ness spread so widely).

@acl-cqc
Copy link
Contributor Author

acl-cqc commented Jul 26, 2023

Closing in favour of #298

@acl-cqc acl-cqc closed this Jul 26, 2023
@acl-cqc acl-cqc deleted the new/buildable_hugrmut branch August 3, 2023 10:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants